home *** CD-ROM | disk | FTP | other *** search
/ Java 1996 August / Java - Summer 1996.iso / classes / animator.java < prev    next >
Text File  |  1996-01-31  |  11KB  |  466 lines

  1. /*
  2.  * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
  3.  *
  4.  * Permission to use, copy, modify, and distribute this software
  5.  * and its documentation for NON-COMMERCIAL purposes and without
  6.  * fee is hereby granted provided that this copyright notice
  7.  * appears in all copies. Please refer to the file "copyright.html"
  8.  * for further important copyright and licensing information.
  9.  *
  10.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  11.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  12.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  13.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  14.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  15.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  16.  */
  17. /*
  18.  * @(#)Animator.java    1.3 95/09/29 Herb Jellinek
  19.  *
  20.  * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved.
  21.  *
  22.  * Permission to use, copy, modify, and distribute this software
  23.  * and its documentation for NON-COMMERCIAL purposes and without
  24.  * fee is hereby granted provided that this copyright notice
  25.  * appears in all copies. Please refer to the file "copyright.html"
  26.  * for further important copyright and licensing information.
  27.  *
  28.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  29.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  30.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  31.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  32.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  33.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  34.  */
  35.  
  36. import java.io.InputStream;
  37. import awt.*;
  38. import browser.Applet;
  39. import browser.audio.*;
  40. import java.util.Vector;
  41. import java.io.File;
  42. import net.www.html.*;
  43.  
  44.  
  45. /**
  46.  * An applet that plays a sequence of images, as a loop or a one-shot.
  47.  * Can have a soundtrack and/or sound effects tied to individual frames.
  48.  *
  49.  * @author James Gosling (original ImageLoopItem.java)
  50.  * @author Herb Jellinek
  51.  * @version 1.3, 29 Sep 1995
  52.  */
  53.  
  54. class Animator extends Applet implements Runnable {
  55.     
  56.     /**
  57.      * The images.
  58.      */
  59.     Vector images;
  60.  
  61.     /**
  62.      * Order in which to display them, time and sound for each one.
  63.      */
  64.     Vector order;
  65.  
  66.     /**
  67.      * Largest width.
  68.      */
  69.     int maxWidth = 0;
  70.  
  71.     /**
  72.      * Largest height.
  73.      */
  74.     int maxHeight = 0;
  75.  
  76.     /**
  77.      * The directory or URL from which the images are loaded
  78.      */
  79.     String dir;
  80.  
  81.     /**
  82.      * The thread animating the images.
  83.      */
  84.     Thread engine = null;
  85.  
  86.     /**
  87.      * The current loop slot - index into 'order.'
  88.      */
  89.     int frameNum = 0;
  90.  
  91.     /**
  92.      * The default number of milliseconds to wait between frames.
  93.      */
  94.     public static final int defaultPause = 3900;
  95.     
  96.     /**
  97.      * The global delay between images, which can be overridden by
  98.      * the PAUSE attribute.
  99.      */
  100.     int globalPause = defaultPause;
  101.  
  102.     /**
  103.      * The soundtrack.
  104.      */
  105.     InputStream audioStream;
  106.  
  107.     /**
  108.      * Whether or not the thread has been paused by the user.
  109.      */
  110.     boolean userPause = false;
  111.  
  112.     /**
  113.      * Repeat the animation?  If false, just play it once.
  114.      */
  115.     boolean repeat;
  116.  
  117.     /**
  118.      * Can this version of the browser do double buffered graphics?
  119.      */
  120.     boolean canDoubleBuffer;
  121.     
  122.     /**
  123.      * The offscreen image, used in double buffering
  124.      */
  125.     Image offScrImage;
  126.  
  127.     /**
  128.      * The offscreen graphics context, used in double buffering
  129.      */
  130.     Graphics offScrGC;
  131.  
  132.     /**
  133.      * Can we paint yet?
  134.      */
  135.     boolean loaded = false;
  136.  
  137.     /**
  138.      * "Loading" message.
  139.      */
  140.     String loadingMessage = "Wait...";
  141.     
  142.     /**
  143.      * Draw the loading message, or draw the background?
  144.      */
  145.     boolean showMessage = true;
  146.     
  147.     boolean debug = false;
  148.  
  149.     /**
  150.      * Load the images.
  151.      * @param dir directory to load from. The images are assumed
  152.      *   to be named T1.gif, T2.gif...
  153.      *
  154.      * Sets maxWidth, maxHeight to be those of the largest image.
  155.      * Sets images to contain the GIFs.
  156.      * If order vector is null, create a default one.
  157.      */
  158.     void loadImages(URL context, String dir) {
  159.     images = new Vector(10);
  160.     for (int i = 1; ; i++) {
  161.         showLoadingMsg();
  162.         Image im = getImage(dir+File.separator+"/T"+i+".gif");
  163.  
  164.         if (im == null) {
  165.         break;
  166.         }
  167.  
  168.         if (debug) {
  169.             System.out.print("Got image T"+i+".gif; ");
  170.         }
  171.  
  172.         images.addElement(im);
  173.         if (debug) {
  174.             System.out.println("made it image "
  175.                     + (images.size() - 1) + ".");
  176.         }
  177.         if (im.width > maxWidth) {
  178.         maxWidth = im.width;
  179.         }
  180.         if (im.height > maxHeight) {
  181.         maxHeight = im.height;
  182.         }
  183.     }
  184.     if (order == null) {
  185.         order = new Vector(images.size());
  186.         for (int i = 0; i < images.size(); i++) {
  187.         order.addElement(new FrameSpec(i, globalPause, null));
  188.             if (debug) {
  189.                 System.out.println("Added image " + i + 
  190.                        " to order Vector at position "
  191.                        + (order.size() - 1) + ".");
  192.             }
  193.         }
  194.     }
  195.     }
  196.  
  197.     /**
  198.      * Parse the ORDER attribute.  It looks like
  199.      * 1|2|3|4|5, etc., where each number (item) refers to a source image.
  200.      * Each item can also be of the form nn:mm, where mm is the number
  201.      * of milliseconds to show that image, or nn:mm@xxxxx, where xxxx is the
  202.      * URL for a sound effect to accompany that image.
  203.      */
  204.     void parseOrder(String attr) {
  205.     if (attr != null) {
  206.         order = new Vector(10);
  207.         for (int i = 0; i < attr.length(); ) {
  208.         int next = attr.indexOf('|', i);
  209.         if (next == -1) next = attr.length();
  210.         parseItem(attr.substring(i, next));
  211.         i = next + 1;
  212.         }
  213.     }
  214.     }
  215.  
  216.     void parseItem(String item) {
  217.     // Cases: nothing but a frame number
  218.     //        imageno:pause
  219.     //        imageno:pause@audio
  220.     //        imageno@audio
  221.  
  222.     int pause = globalPause;
  223.     int imageNum;
  224.     String audio = null;
  225.     
  226.     int colonPos = item.indexOf(':');
  227.     int atPos = item.indexOf('@');
  228.  
  229.     if (atPos > 0) {
  230.         if (colonPos < 0) {
  231.         imageNum = Integer.parseInt(item.substring(0, atPos)) - 1;
  232.         audio = item.substring(atPos + 1);
  233.         } else {
  234.         imageNum = Integer.parseInt(item.substring(0, colonPos)) - 1;
  235.         pause = Integer.parseInt(item.substring(colonPos + 1, atPos));
  236.         audio = item.substring(atPos + 1);
  237.         }
  238.     } else {
  239.         if (colonPos < 0) {
  240.         imageNum = Integer.parseInt(item) - 1;
  241.         } else {
  242.         imageNum = Integer.parseInt(item.substring(0, colonPos)) - 1;
  243.         pause = Integer.parseInt(item.substring(colonPos + 1));
  244.         }
  245.     }
  246.  
  247.     if (audio != null) {
  248.         tellLoadingMsg(audio);
  249.     }
  250.     order.addElement(new FrameSpec(imageNum, pause,
  251.                        (audio == null) ? null :
  252.                        getAudioData(new URL(documentURL,
  253.                                 audio))));
  254.     if (debug) {
  255.         System.out.println("Added image " + imageNum + 
  256.                        " to order Vector at position "
  257.                        + (order.size() - 1) + ".");
  258.     }
  259.     }
  260.  
  261.     
  262.     /**
  263.      * Initialize the applet.  Get attributes.
  264.      */
  265.     public void init() {
  266.     String attr = getAttribute("IMG");
  267.     dir = (attr != null) ? attr : "doc:/demo/images/duke";
  268.     
  269.     attr = getAttribute("PAUSE");
  270.     globalPause = (attr != null) ? Integer.parseInt(attr) : defaultPause;
  271.  
  272.     attr = getAttribute("REPEAT");
  273.     repeat = (attr == null) ? true : (attr.equalsIgnoreCase("yes") ||
  274.                       attr.equalsIgnoreCase("true"));
  275.     
  276.     attr = getAttribute("ORDER");
  277.     parseOrder(attr);
  278.  
  279.     attr = getAttribute("AUDIOLOOPS");
  280.     boolean audioLoops =
  281.         (attr == null) ? false : (attr.equalsIgnoreCase("yes") ||
  282.                       attr.equalsIgnoreCase("true"));
  283.     attr = getAttribute("AUDIO");
  284.     if (attr != null) {
  285.         URL url = new URL(documentURL, attr);
  286.         showLoadingMsg();
  287.         if (audioLoops) {
  288.         audioStream = getContinuousAudioStream(url);
  289.         } else {
  290.         audioStream = getAudioStream(url);
  291.         }
  292.     }
  293.  
  294.     canDoubleBuffer = true;
  295.     try {
  296.         // for when/if browser.hotjava goes away
  297.         canDoubleBuffer = !browser.hotjava.version.equals("1.0 alpha2");
  298.     } catch (Exception e) { }
  299.     }
  300.  
  301.     void tellLoadingMsg(String file) {
  302.     showStatus("Loading file "+file);
  303.     }
  304.  
  305.     void showLoadingMsg() {
  306.     repaint();
  307.     showMessage = !showMessage;
  308.     }
  309.  
  310.     void drawLoadingMessage(Graphics g) {
  311.     if (showMessage) {
  312.         g.drawString(loadingMessage, 0, height / 2);
  313.     } else {
  314.         g.clearRect(0, 0, width, height);
  315.     }
  316.     }
  317.         
  318.  
  319.     void startPlaying() {
  320.     startPlaying(audioStream);
  321.     }
  322.  
  323.     void stopPlaying() {
  324.     stopPlaying(audioStream);
  325.     }
  326.  
  327.     /**
  328.      * Run the animation. This method is called by class Thread.
  329.      * @see java.lang.Thread
  330.      */
  331.     public void run() {
  332.     Thread me = Thread.currentThread();
  333.     me.setPriority(Thread.MIN_PRIORITY);
  334.  
  335.     loadImages(documentURL, dir);
  336.     
  337.     resize(maxWidth, maxHeight);
  338.     if (canDoubleBuffer) {
  339.         offScrImage = createImage(maxWidth, maxHeight);
  340.         offScrGC = new Graphics(offScrImage);
  341.         offScrGC.setForeground(Color.lightGray);
  342.     }
  343.  
  344.     loaded = true;
  345.  
  346.     if (userPause) {
  347.         return;
  348.     }
  349.  
  350.     if (frameNum < order.size()) {
  351.         startPlaying();
  352.     }
  353.  
  354.     try {    
  355.         if (images.size() > 1) {
  356.         while (width > 0 && height > 0 && engine == me) {
  357.             if (frameNum >= order.size()) {
  358.             if (!repeat) {
  359.                 return;
  360.             }
  361.             frameNum = 0;
  362.             }
  363.             repaint();
  364.             FrameSpec spec = (FrameSpec)order.elementAt(frameNum++);
  365.             if (spec.audio != null) {
  366.             play(spec.audio);
  367.             }
  368.             Thread.sleep(100 + spec.pause);
  369.         }
  370.         }
  371.     } finally {
  372.         stopPlaying();
  373.     }
  374.     }
  375.  
  376.     /**
  377.      * Paint the current frame.
  378.      */
  379.     public void paint(Graphics g) {
  380.     update(g);
  381.     }
  382.  
  383.     public void update(Graphics g) {
  384.     if (! loaded) {
  385.         drawLoadingMessage(g);
  386.     } else {
  387.         if (images != null) {
  388.         if (frameNum < order.size()) {
  389.             if (canDoubleBuffer) {
  390.             offScrGC.fillRect(0, 0, width, height);
  391.             } else {
  392.             g.clearRect(0, 0, width, height);
  393.             }
  394.             FrameSpec spec = (FrameSpec)order.elementAt(frameNum);
  395.             if (debug) {
  396.             System.out.print("Drawing frame "+frameNum+": ");
  397.             }
  398.             if (spec.num < images.size()) {
  399.                 if (debug) {
  400.                 System.out.println("image "+spec.num+".");
  401.                 }
  402.             Image image = (Image)images.elementAt(spec.num);
  403.             if (canDoubleBuffer) {
  404.                 offScrGC.drawImage(image, 0, 0);
  405.                 g.drawImage(offScrImage, 0, 0);
  406.             } else {
  407.                 g.drawImage(image, 0, 0);
  408.             }
  409.             }
  410.         } else {
  411.             // no more animation, but need to draw something
  412.             if (debug) {
  413.             System.out.print("No more animation; ");
  414.             System.out.println("drawing image "+ 0 + ".");
  415.             }
  416.             g.drawImage((Image)images.elementAt(0), 0, 0);
  417.         }
  418.         }
  419.     }
  420.     }
  421.  
  422.     /**
  423.      * Start the applet by forking an animation thread.
  424.      */
  425.     public void start() {
  426.     if (engine == null) {
  427.         engine = new Thread(this);
  428.         engine.start();
  429.     }
  430.     }
  431.  
  432.     /**
  433.      * Stop the insanity, um, applet.
  434.      */
  435.     public void stop() {
  436.     if (engine != null && engine.isAlive()) {
  437.         engine.stop();
  438.     }
  439.     engine = null;
  440.     }
  441.  
  442.     /**
  443.      * Pause the thread when the user clicks the mouse in the applet.
  444.      * If the thread has stopped (as in a non-repeat performance), restart it.
  445.      */
  446.     public void mouseDown(int x, int y) {
  447.     if (loaded) {
  448.         if (engine != null && engine.isAlive()) {
  449.         if (userPause) {
  450.             engine.resume();
  451.             startPlaying();
  452.         } else {
  453.             engine.suspend();
  454.             stopPlaying();
  455.         }
  456.         userPause = !userPause;
  457.         } else {
  458.         userPause = false;
  459.         frameNum = 0;
  460.         engine = new Thread(this);
  461.         engine.start();
  462.         }
  463.     }
  464.     }
  465. }
  466.